<?php
/**
 * Resources Class
 * Used for work with JS and CSS sources
 * @author Kirill A Egorov 2011
 */
class Resource
{
	protected static $_instance = null;
	protected static $_jsCacheUrl = null;
	protected static $_jsCachePath = null;
	protected static $_docRoot = null;
	/**
	 * @var Zend_Cache_Core
	 */
	protected static $_cache = false;
	
	protected $_jsFiles = array();
	protected $_rawFiles = array();
	protected $_cssFiles = array();
	protected $_rawJs = '';
	protected $_rawCss = '';
	protected $_inlineJs = '';

	protected function __construct(){}

	/**
	 * @return Resource
	 */
	public static function getInstance()
	{
		if(is_null(self::$_instance))
			self::$_instance = new self();
		return self::$_instance;
	}

	/**
	 * Set chache core
	 * @param Zend_Cache_Core $manager
	 */
	static public function setCache(Zend_Cache_Core $manager)
	{
		self::$_cache = $manager;
	}

	/**
	 * Define cache paths
	 * @param string $url  - url to cache folder
	 * @param string $path - system path to cache folder
	 */
	static public function setCachePaths($url , $path)
	{
		self::$_jsCachePath = $path;
		self::$_jsCacheUrl = $url;
	}

	/**
	 * Set document root path
	 * @param string  $path
	 */
	static public function setDocRoot($path)
	{
		self::$_docRoot = $path;
	}

	/**
	 * Items sort function
	 * @param object $el1
	 * @param object $el2
	 * @return integer
	 */
	public static function sortingByOrder($el1 , $el2)
	{
		if($el1['order'] == $el2['order'])
			return 0;
		if($el1['order'] < $el2['order'])
			return - 1;
		return 1;
	}

	/**
	 * Add javascript file to the contentent 
	 * @param string $file	- file path relate to document root
	 * @param integer $order - include order
	 * @param boolean $minified - file already minified
	 */
	public function addJs($file , $order = false , $minified = false)
	{
		$hash = md5($file);
		
		if($order === false)
			$order = sizeof($this->_jsFiles);
		
		if(!array_key_exists($hash , $this->_jsFiles))
			$this->_jsFiles[$hash] = array(
					'file' => $file , 
					'order' => $order , 
					'minified' => $minified
			);
	}

	/**
	 * Add css file to the contentent 
	 * @param string $file
	 * @param integer $order
	 */
	public function addCss($file , $order = false)
	{
		$hash = md5($file);
		if($order === false)
			$order = sizeof($this->_cssFiles);
		
		if(!array_key_exists($hash , $this->_cssFiles))
			$this->_cssFiles[$hash] = array(
					'file' => $file , 
					'order' => $order
			);
	}

	public function addRawJs($script)
	{
		$this->_rawJs .= $script;
	}

	/**
	 * Add standalone JS file (no modifications)
	 * @param string $file - file path relative to the document root directory
	 */
	public function addJsRawFile($file)
	{
		if(! in_array($file , $this->_rawFiles , true))
			$this->_rawFiles[] = $file;
	}

	public function addInlineJs($script)
	{
		$this->_inlineJs .= $script;
	}

	public function addRawCss($script)
	{
		$this->_rawCss .= $script;
	}

	/**
	 * Returns javascript source tags. Include order: Files , Raw , Inline
	 * @param boolean $useMin - use Js minify
	 * @param boolean $compile - compile Files into one
	 * @return string
	 */
	public function includeJs($useMin = false , $compile = false)
	{
		$s = '';
		/*
		 * Raw files
		 */
		if(!empty($this->_rawFiles))
			foreach($this->_rawFiles as $file)
				$s .= '<script type="text/javascript" src="' . $file . '"></script>' . "\n";
		/*
    	 * javascript files
    	 */
		if(!empty($this->_jsFiles))
		{
			uasort($this->_jsFiles , array(
					'self' , 
					'sortingByOrder'
			));
			if($compile)
				$s .= '<script type="text/javascript" src="' . $this->_compileJsFiles($this->_jsFiles , $useMin) . '"></script>' . "\n";
			else
				foreach($this->_jsFiles as $file)
					$s .= '<script type="text/javascript" src="' . $file['file'] . '"></script>' . "\n";
		}
		/*
		 * Raw javascript
		 */
		if(strlen($this->_rawJs))
		{
			$hash = md5($this->_rawJs);
			$cacheFile = $hash . '.js';
			if(!file_exists(self::$_jsCachePath . $cacheFile))
			{
				if($useMin)
					$this->_rawJs = Code_Js_Minify::minify($this->_rawJs);
				file_put_contents(self::$_jsCachePath . $cacheFile , $this->_rawJs);
			}
			$s .= '<script type="text/javascript" src="' . self::$_jsCacheUrl . $cacheFile . '"></script>' . "\n";
		}
		/*
		 * Inline javascript
		 */
		if(!empty($this->_inlineJs))
		{
			if($useMin)
				$this->_inlineJs = Code_Js_Minify::minify($this->_inlineJs);
			$s .= '<script language="javascript">' . "\n" . $this->_inlineJs . "\n" . ' </script>' . "\n";
		}
		return $s;
	}

	/**
	 * Compile JS files cache
	 * @param array $files - file paths relative to the document root directory
	 * @param boolean $minify - minify scripts
	 * @return string  - cached file path
	 */
	protected function _compileJsFiles($files , $minify)
	{
		$validHash = $this->_getFileHash(Utils::fetchCol('file' , $files));
		$cachedUrl = self::$_jsCacheUrl . $validHash . '.js';
		
		if(file_exists(self::$_jsCachePath . $validHash . '.js'))
			return $cachedUrl;
		
		$str = '';
		foreach($files as $file)
		{
			$str .= "\n";
			if($minify && ! $file['minified'])
				$str .= Code_Js_Minify::minify(file_get_contents(self::$_docRoot . $file['file']));
			else
				$str .= file_get_contents(self::$_docRoot . $file['file']);
		}
		file_put_contents(self::$_jsCachePath . $validHash . '.js' , $str);
		return $cachedUrl;
	}

	/**
	 * Get a hash for the file list. Used to check for changes in files.
	 * @param array $files - File paths relative to the document root directory
	 * @return string
	 */
	protected function _getFileHash($array)
	{
		$listHash = md5(serialize($array));
		/*
		 * Checking if hash is cached 
		 * (IO operations is too expensive)
		 */
		if(self::$_cache)
		{
			$dataHash = self::$_cache->load($listHash);
			if($dataHash)
				return $dataHash;
		}
		
		$dataHash = '';
		foreach($array as $file)
			$dataHash .= $file . ':' . filemtime(self::$_docRoot . $file);
		
		if(self::$_cache)
			self::$_cache->save(md5($dataHash) , $listHash , array(
					'jsFilesHash'
			) , null);
		
		return md5($dataHash);
	}

	public function includeCss()
	{
		$s = '';
		
		if(!empty($this->_cssFiles))
		{
			uasort($this->_cssFiles , array(
					'self' , 
					'sortingByOrder'
			));
			
			foreach($this->_cssFiles as $k => $v)
				$s .= '<link rel="stylesheet" type="text/css" href="' . $v['file'] . '" />' . "\n";
		}
		
		if(strlen($this->_rawCss))
			$s .= '<style type="text/css">' . "\n" . $this->_rawCss . "\n" . '</style>' . "\n";
		
		return $s;
	}
}